package pl.edu.agh.agents.generator;

import java.awt.Color;
import java.awt.Point;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Random;

import pl.edu.agh.agents.agent.Agent;
import pl.edu.agh.agents.agent.AgentStrategy;
import pl.edu.agh.agents.agent.AutonomousAgent;
import pl.edu.agh.agents.gui.GraphSaver;
import pl.edu.agh.agents.gui.GraphUtils;
import pl.edu.agh.agents.state.State;

public class Generator {
	private static final int MAX_DENSITY = 100;

	public static void main(String[] args) {
		if (args.length < 4) {
			System.out
			.println("Usage: Generator filename states agents edge_density (1-"
					+ MAX_DENSITY + ")");
			System.exit(-1);
		}

		String filename = args[0];
		int statesCount = Integer.parseInt(args[1]);
		int agentsCount = Integer.parseInt(args[2]);
		int density = Integer.parseInt(args[3]);

		
		List<State> states = new LinkedList<State>();
		List<Point> points = new LinkedList<Point>();
		List<Agent> agents = new LinkedList<Agent>();
		Map<Agent, Color> agentColors = new HashMap<Agent, Color>();

		generateGraph(statesCount, density, states, points);
		generateAgents(statesCount, agentsCount, states, agents, agentColors);
		
		GraphSaver saver = GraphSaver.getInstance();
		saver.saveGraphToFile(filename, states, points, agents, agentColors);
	}

	private static void generateGraph(int statesCount, int density, List<State> states, List<Point> points) {
		Random random = new Random();

		// states and their coordinates
		for (int i = 0; i < statesCount; i++) {
			int x = random.nextInt(GraphUtils.GRAPH_WIDTH);
			int y = random.nextInt(GraphUtils.GRAPH_HEIGHT);

			State state = new State(i, false, 1.0);
			Point point = new Point(x, y);

			states.add(state);
			points.add(point);
		}
		int step = statesCount * density / 100 - 1;
		for (State state : states) {
			List<State> nearestPoints = getNearestId(step, state, states, points);

			for (State neighbor : nearestPoints) {
				states.get(state.getID()).addNeighbor(states.get(neighbor.getID()));
			}
		}
	}

	private static List<State> getNearestId(int number, State state, List<State> states, List<Point> points) {
		PriorityQueue<Double> dist = new PriorityQueue<Double>();
		Map<Double, State> distStateMap = new HashMap<Double, State>();
		Point point = points.get(state.getID());
		for (State secondState : states) {
			if (secondState.equals(state)) {
				continue;
			}

			Point secondPoint = points.get(secondState.getID());
			double xPow = Math.pow(point.getX() - secondPoint.getX(), 2);
			double yPow = Math.pow(point.getY() - secondPoint.getY(), 2);
			double distance = Math.sqrt(xPow + yPow);
			dist.add(distance);
			distStateMap.put(distance, secondState);

		}

		List<State> nearest = new ArrayList<State>();
		for (int i = 0; i < number; i++) {
			double distance = dist.poll();
			nearest.add(distStateMap.get(distance));
		}

		return nearest;
	}

	private static List<Agent> generateAgents(int statesCount, int agentsCount, List<State> states, List<Agent> agents,
			Map<Agent, Color> agentColors) {
		Random random = new Random();

		List<Integer> currentStates = new ArrayList<Integer>();
		List<Integer> goalStates = new ArrayList<Integer>();

		for (int i = 0; i < statesCount; i++) {
			currentStates.add(i);
			goalStates.add(i);
		}

		for (int i = 0; i < agentsCount; i++) {
			int s = random.nextInt(currentStates.size());
			int currentState = currentStates.get(s);
			currentStates.remove(s);

			s = random.nextInt(goalStates.size());
			int goalState = goalStates.get(s);
			while (currentState == goalState) {
				s = random.nextInt(goalStates.size());
				goalState = goalStates.get(s);
			}
			goalStates.remove(s);

			Agent agent = new AutonomousAgent();
			agent.setID(i);
			agent.setStartState(states.get(currentState));
			agent.setCurrentState(states.get(currentState));
			agent.setGoalState(states.get(goalState));
			agent.setLookAheadStates(0);
			agent.setStrategy(AgentStrategy.Offensive);
			agents.add(agent);
			
			agentColors.put(agent, GraphUtils.DEFAULT_AGENT_COLOR);
		}

		return agents;
	}
}